package com.open.capacity.finance.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.open.capacity.finance.config.Globals;
import com.open.capacity.finance.dao.*;
import com.open.capacity.finance.dto.AuditModelBaseResult;
import com.open.capacity.finance.dto.SourcePzErrorDto;
import com.open.capacity.finance.dto.audit_model_41.AuditModelResult;
import com.open.capacity.finance.dto.audit_model_41.ExcelData;
import com.open.capacity.finance.entity.AuditModel;
import com.open.capacity.finance.entity.AuditModelRule;
import com.open.capacity.finance.entity.SourcePz;
import com.open.capacity.finance.service.AbstractAuditMakeResultFactory;
import com.open.capacity.finance.service.AuditModelRuleService;
import com.open.capacity.finance.service.AuditModelService;
import com.open.capacity.finance.service.SourcePzService;
import com.open.capacity.finance.util.FileUtil;
import com.open.capacity.user.model.SysCompany;
import com.open.capacity.utils.StringUtil;
import com.open.capacity.utils.encrypt.Md5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AuditModelActuator41ServiceImpl<T extends AuditModelBaseResult> extends AbstractAuditMakeResultFactory {

    @Autowired
    private AuditAnalysisDao analysisDao;

    @Autowired
    private SourcePzService pzService;

    @Autowired
    private SourceBaseKjkmExtDao kjkmExtDao;
    @Autowired
    private SourceBaseCustomItemDao customItemDao;
    @Autowired
    private SourceBaseCustomValueDao customValueDao;

    @Autowired
    private AuditModelRuleService ruleService;
    @Autowired
    private AuditModelService modelService;


    @Override
    protected T analyseResultData(String modelId, Map param) {
        String companyId = param.containsKey("companyId") ? param.get("companyId").toString() : "";
        param.put("ztId", "101");
        AuditModelResult result = new AuditModelResult();
        AuditModel model = modelService.findById(modelId);
        Assert.notNull(model, "找不到该模型");
        List<AuditModelRule> rules = ruleService.findByModelId(modelId);
        String errorCode = param.containsKey("errorCode") ? param.get("errorCode").toString() : "";
        rules.forEach(t -> {
            param.put(t.getConfKey(), t.getConfValue());
        });
        result.setModelName(model.getCodeAndName());
        List<SourcePz> pzList = pzService.findPz(param);
        List<SourcePz> pzListext = pzService.findExt(param);
        pzList.forEach(t -> {
            if (t.getKjkmCode().startsWith("1") || t.getKjkmCode().startsWith("3") || t.getKjkmCode().startsWith("5") || t.getKjkmCode().startsWith("7")) {
                t.setYe(t.getJ().subtract(t.getD()));
            } else {
                t.setYe(t.getD().subtract(t.getJ()));
            }
        });
        List<SourcePzErrorDto> step1List = new ArrayList<>();
//        5.1. 收入科目检查：
//        a) 会计科目：  1011 = 800101	 总发生额。
//        b) 会计科目    6001 = 4001
//        c) 会计科目    4601 = 6601
//        d) 会计科目    4604 = 6604
//        e) 会计科目    4901 = 6901
//        List<SourcePzErrorDto> errorList1 = pzService.checkKmEqual(pzList);

//        5.2. 检查负数情况：
//        a) 1,5,7开头的会计科目借方余额不能为负数，贷方余额不能有数   (排除1602，1702*)
//        b) 2,3,4,6,8 开头的会计科目贷方余额不能为负数，借方余额不能有数 (排除3001*，3301*)
        List<SourcePzErrorDto> errorList2 = pzService.checkKmNegative(pzList);

        //    检查年初负数
//        List<SourcePzErrorDto> errorList3 = pzService.checkKmNcyeNegative(pzList);

//        5.4  经济分类支出检查
//        a) 7开头的7* 辅助项记录余额（默认只有借方额）不能为负数。
//        List<SourcePzErrorDto> errorList4 = pzService.checkJjkmNegative(pzListext);

//        5.5  项目检查
//        a) 预算项目维度：科目核算  8开头的8*（剔除8001开头的8001*）的期初数 + 6开头的6* 的余额 - 7开头的7*余额 结果不能为负数
//        List<SourcePzErrorDto> errorList5 = pzService.checkXmNegative(pzListext);



        if (StringUtil.isNotEmpty(errorCode)) {
            switch (errorCode) {
//                case "1":
//                    step1List.addAll(errorList1);
//                    break;
                case "2":
                    step1List.addAll(errorList2);
                    break;
//                case "3":
//                    step1List.addAll(errorList3);
//                    break;
//                case "4":
//                    step1List.addAll(errorList4);
//                    break;
//                case "5":
//                    step1List.addAll(errorList5);
//                    break;

            }
        } else {
//            step1List.addAll(errorList1);
            step1List.addAll(errorList2);
//            step1List.addAll(errorList3);
//            step1List.addAll(errorList4);
//            step1List.addAll(errorList5);
        }

        result.setCompanyId(companyId);
        step1List.forEach(t -> {
            Optional<SysCompany> first = Globals.companyList.stream().filter(c -> c.getId().equals(t.getCompanyId())).findFirst();
            if (first.isPresent()) {
                t.setCompanyName(first.get().getFullName());
            }
        });

        step1List.sort(Comparator.comparing(SourcePzErrorDto::getCompanyId));

        result.setStep1List(step1List);
        if (CollectionUtils.isEmpty(step1List)) {
            result.setExists(0);
        } else {
            result.setExists(1);

            //持久化本地
            persistence(result);
        }

        return (T) result;
    }


    @Override
    protected int persistence(AuditModelBaseResult data) {
        int result = 0;

        String content = JSON.toJSONString(data, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat);

        data.setResultFileMd5(Md5Utils.hash(content));
        data.setResultFileUrl("/upload/analyse/model41_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");

        Writer write = null;
        try {
            File file = new File(fileuploadPath + "/upload/analyse/model41_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            // 写入文件
            write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
            write.write(content);
            write.flush();
            write.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                write.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return result;
    }

    @Override
    public T getData(String modelId, Map param) {
        return analyseResultData(modelId, param);
    }


    /**
     * 从本地加载数据
     */
    public AuditModelBaseResult getDataFromJson(String jsonUrl, Map param) {
        AuditModelResult result = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            result = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        filterCondition(result, param);

        return result;
    }

    /**
     * 本地json数据条件过滤
     */
    private AuditModelResult filterCondition(AuditModelResult orig, Map param) {
        AuditModelResult result = orig;
        String companyId = MapUtils.getString(param, "companyId");
        Integer kjnd = MapUtils.getInteger(param, "kjnd");
        Integer errorCode = MapUtils.getInteger(param, "errorCode");

        List<SourcePzErrorDto> list = orig.getStep1List();
        if (StringUtil.isNotEmpty(companyId)) {
            list = list.stream().filter(t -> t.getCompanyId().equals(companyId)).collect(Collectors.toList());
        }
        if (kjnd != null) {
            list = list.stream().filter(t -> t.getKjnd().equals(kjnd)).collect(Collectors.toList());
        }
        if (errorCode != null) {
            list = list.stream().filter(t -> t.getErrorCode().equals(errorCode)).collect(Collectors.toList());
        }

        result.setStep1List(list);

        return result;
    }

    public void exportExcel(String modelId, Map params, HttpServletResponse response) {
        AuditModelResult t = (AuditModelResult) analyseResultData(modelId, params);

        export(t, response);
    }

    @Override
    public void exportExcelFromJson(String modelId, String jsonUrl, HttpServletResponse response) {
        AuditModelResult t = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            t = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        export(t, response);
    }

    private void export(AuditModelResult t, HttpServletResponse response) {
        List<ExcelData> result = t.getStep1List().stream().map(item -> {
            ExcelData model = new ExcelData();
            model.setCompanyName(item.getCompanyName());
            model.setKjnd(item.getKjnd());
            model.setErrorCode(item.getErrorCode());
            model.setErrorMessage(item.getErrorMsg());
            return model;
        }).collect(Collectors.toList());

        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-disposition", "attachment;filename=download_41.xls");
        OutputStream ouputStream = null;
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(),
                ExcelData.class, result);
        try {
            ouputStream = response.getOutputStream();
            workbook.write(ouputStream);
        } catch (Exception e) {
            throw new RuntimeException("系统异常");
        } finally {
            try {
                if (ouputStream != null) {
                    ouputStream.flush();
                    ouputStream.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("系统异常");
            }
        }
    }
}
